﻿using CommonNet48.LogUtils.Log4net;
using CommonNet48.Timer;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CommonNet48.ConnectionUtils
{
    /// <summary>
    /// 串口设备抽象类
    /// </summary>
    public abstract class SerialPortDevice : IDisposable
    {
        /// <summary>
        /// 构造
        /// </summary>
        public SerialPortDevice()
        {
            _Logger = new Log4(this.GetType());
        }

        /// <summary>
        /// 日志操作对象
        /// </summary>
        private readonly Log4 _Logger;

        /// <summary>
        /// 线程锁对象
        /// </summary>
        private readonly object _LockObj = new object();

        /// <summary>
        /// 串口对象
        /// </summary>
        protected SerialPort _Port = null;

        /// <summary>
        /// 串口参数字符串(例如9600,N,8,1)
        /// </summary>
        protected string _PortParametersString = string.Empty;

        /// <summary>
        /// 串口名称
        /// </summary>
        protected string _PortName = string.Empty;

        /// <summary>
        /// 终止标记
        /// </summary>
        protected bool _StopFlag = false;

        /// <summary>
        /// 实例化串口并打开
        /// </summary>
        /// <param name="portName">要使用的端口(例如COM1)</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">奇偶校验位(System.IO.Ports.Parity枚举类型之一)</param>
        /// <param name="dataBits">数据位(通常为8)</param>
        /// <param name="stopBits">停止位数目(System.IO.Ports.StopBits枚举类型之一)</param>
        /// <param name="sNewLine">新行分隔字符串，默认为"\r\n"(0x0D,0x0A)</param>
        /// <returns></returns>
        public virtual bool InitPort(string portName, int baudRate, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One, string sNewLine = "\r\n")
        {
            var pName = portName.ToUpper();
            try
            {
                var sPortPars = PortParsToString(baudRate, parity, dataBits, stopBits);
                if (_Port == null)
                {
                    _Port = new SerialPort(pName, baudRate, parity, dataBits, stopBits) { NewLine = sNewLine };
                    _PortName = pName;
                    _PortParametersString = sPortPars;
                }
                else
                {
                    if (sPortPars != _PortParametersString || _PortName != pName)
                    {
                        if (_Port.IsOpen) _Port.Close();
                        _Port = new SerialPort(pName, baudRate, parity, dataBits, stopBits) { NewLine = sNewLine };
                        _PortName = pName;
                        _PortParametersString = sPortPars;
                    }
                }
                if (!_Port.IsOpen) _Port.Open();
                return _Port.IsOpen;
            }
            catch (Exception ex)
            {
                _Logger.Error($"打开串口{pName}失败！\r\nP{ex.Message}", ex);
                return false;
            }
        }

        /// <summary>
        /// 实例化串口并打开
        /// </summary>
        /// <param name="portName">要使用的端口(例如COM1)</param>
        /// <param name="sPortPars">串口参数字符串(例如9600,N,8,1)</param>
        /// <param name="sNewLine">新行分隔字符串，默认为"\r\n"(0x0D,0x0A)</param>
        /// <returns></returns>
        public virtual bool InitPort(string portName, string sPortPars, string sNewLine = "\r\n")
        {
            var pars = sPortPars.Replace('，', ',').ToUpper();
            var arrPars = pars.Split(',');
            if (arrPars.Length != 4) return false;
            if (sPortPars != _PortParametersString)
            {
                int baudRate = Convert.ToInt32(arrPars[0]);
                Parity parity = ParityStrToEnu(arrPars[1]);
                int dataBits = Convert.ToInt32(arrPars[2]);
                StopBits stopBits = StopBitsStrToEnu(arrPars[3]);
                return InitPort(portName, baudRate, parity, dataBits, stopBits, sNewLine);
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 串口参数转换为字符串参数
        /// </summary>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">奇偶校验位(System.IO.Ports.Parity枚举类型之一)</param>
        /// <param name="dataBits">数据位(通常为8)</param>
        /// <param name="stopBits">停止位数目(System.IO.Ports.StopBits枚举类型之一)</param>
        /// <returns></returns>
        protected virtual string PortParsToString(int baudRate, Parity parity, int dataBits, StopBits stopBits)
        {
            return $"{baudRate},{parity.ToString().Substring(0, 1).ToUpper()},{dataBits},{StopBitsToString(stopBits)}";
        }

        /// <summary>
        /// 停止位数目转换为字符串参数
        /// </summary>
        /// <param name="stopBits">停止位数目(System.IO.Ports.StopBits枚举类型之一)</param>
        /// <returns></returns>
        protected virtual string StopBitsToString(StopBits stopBits)
        {
            switch (stopBits)
            {
                case StopBits.None:
                    return "N";
                case StopBits.Two:
                    return "2";
                case StopBits.OnePointFive:
                    return "1.5";
                default:
                    return "1";
            }
        }

        /// <summary>
        /// 字符串奇偶校验位参数转System.IO.Ports.Parity枚举类型
        /// </summary>
        /// <param name="sParity">字符串奇偶校验位参数</param>
        /// <returns></returns>
        protected virtual Parity ParityStrToEnu(string sParity)
        {
            switch (sParity.ToUpper())
            {
                default:
                    return Parity.None;
                case "E":
                    return Parity.Even;
                case "O":
                    return Parity.Odd;
                case "S":
                    return Parity.Space;
                case "M":
                    return Parity.Mark;
            }
        }

        /// <summary>
        /// 字符串停止位数目参数转System.IO.Ports.StopBits枚举类型
        /// </summary>
        /// <param name="sStopBits"></param>
        /// <returns></returns>
        protected StopBits StopBitsStrToEnu(string sStopBits)
        {
            switch (sStopBits.Trim())
            {
                default:
                    return StopBits.None;
                case "1":
                    return StopBits.One;
                case "2":
                    return StopBits.Two;
                case "1.5":
                    return StopBits.OnePointFive;
            }

        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <returns></returns>
        protected virtual bool OpenPort()
        {
            try
            {
                if (_Port == null) return false;
                if (!_Port.IsOpen) _Port.Open();
                return _Port.IsOpen;
            }
            catch (Exception ex)
            {
                _Logger.Error($"打开串口{_Port.PortName}失败！\r\n{ex.Message}", ex);
                return false;
            }

        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        protected virtual void ClosePort()
        {
            if (_Port != null && _Port.IsOpen) _Port.Close();
        }

        /// <summary>
        /// 发送数据(字符串)
        /// </summary>
        /// <param name="data">要发送的字符串</param>
        /// <param name="isClosePort">发送完是否关闭串口</param>
        /// <returns></returns>
        protected virtual bool SendData(string data, bool isClosePort = false)
        {
            try
            {
                lock (_LockObj)
                {
                    _StopFlag = false;
                    if (!OpenPort()) return false;
                    _Port.Write(data);
                    if (isClosePort) ClosePort();
                    return true;
                }
            }
            catch (Exception ex)
            {
                ClosePort();
                this._Logger.Error($"发送字符串数据({data})失败！\r\n{ex.Message}", ex);
                return false;
            }
        }

        /// <summary>
        /// 发送数据(字节数组)
        /// </summary>
        /// <param name="bytes">要发送的字节数组</param>
        /// <param name="isClosePort">发送完是否关闭串口</param>
        /// <returns></returns>
        protected virtual bool SendData(byte[] bytes, bool isClosePort = false)
        {
            try
            {
                lock (_LockObj)
                {
                    _StopFlag = false;
                    if (!OpenPort()) return false;
                    _Port.Write(bytes, 0, bytes.Length);
                    if (isClosePort) ClosePort();
                    return true;
                }
            }
            catch (Exception ex)
            {
                ClosePort();
                this._Logger.Error($"发送字节数组({string.Join(" ", bytes.Select(t => $"0x{t:X2}"))})失败！\r\n{ex.Message}", ex);
                return false;
            }
        }

        /// <summary>
        /// 发送字节数组并读取返字节集合
        /// </summary>
        /// <param name="bytes">要发送的字节数组</param>
        /// <param name="readFunc">读取业务委托</param>
        /// <param name="sendDelay">发送后等待回读时间，单位毫秒。</param>
        /// <param name="isClosePort">发送完是否关闭串口</param>
        /// <returns></returns>
        protected virtual IList<byte> SendAndRead(byte[] bytes, Func<SerialPort, IList<byte>> readFunc, long sendDelay = 0, bool isClosePort = false)
        {
            MyTimer timer = new MyTimer();
            try
            {
                lock (_LockObj)
                {
                    _StopFlag = false;
                    timer.AbortFun = () => _StopFlag;
                    if (!OpenPort()) return null;
                    _Port.Write(bytes, 0, bytes.Length);
                    if (sendDelay > 0) timer.Delay(sendDelay);
                    if (_StopFlag) return null;
                    var readBytes = readFunc(_Port);
                    if (isClosePort) ClosePort();
                    if (readBytes != null && readBytes.Count > 0)
                    {
                        return readBytes;
                    }
                    else
                    {
                        return null;
                    }
                }
            }
            catch (Exception ex)
            {
                ClosePort();
                this._Logger.Error($"发送字节数组({string.Join(" ", bytes.Select(t => $"0x{t:X2}"))})并读取返回数据失败！\r\n{ex.Message}", ex);
                return null;
            }
        }

        /// <summary>
        /// 按行发送数据(字符串)
        /// </summary>
        /// <param name="data">要发送的字符串</param>
        /// <param name="iSendOut">发送超时时间，单位毫秒</param>
        /// <param name="sNewLine">行分隔字符串</param>
        /// <param name="isClosePort">发送完是否关闭串口</param>
        /// <returns></returns>
        protected virtual bool SendDataLine(string data, int iSendOut, string sNewLine = "\r\n", bool isClosePort = false)
        {
            try
            {
                lock (_LockObj)
                {
                    _StopFlag = false;
                    if (!OpenPort()) return false;
                    if (!string.IsNullOrWhiteSpace(sNewLine)) _Port.NewLine = sNewLine;
                    _Port.WriteTimeout = iSendOut;
                    _Port.WriteLine(data);
                    if (isClosePort) ClosePort();
                    return true;
                }
            }
            catch (Exception ex)
            {
                ClosePort();
                this._Logger.Error($"发送字符串数据({data})失败！\r\n{ex.Message}", ex);
                return false;
            }
        }

        /// <summary>
        /// 按行发送数据(字节数组)
        /// </summary>
        /// <param name="bytes">要发送的字节数组</param>
        /// <param name="iSendOut">发送超时时间，单位毫秒</param>
        /// <param name="sNewLine">行分隔字符串</param>
        /// <param name="isClosePort">发送完是否关闭串口</param>
        /// <returns></returns>
        protected virtual bool SendDataLine(byte[] bytes, int iSendOut, string sNewLine = "\r\n", bool isClosePort = false)
        {
            try
            {
                string data = Encoding.ASCII.GetString(bytes);
                return SendDataLine(data, iSendOut, sNewLine, isClosePort);
            }
            catch (Exception ex)
            {
                ClosePort();
                this._Logger.Error($"发送字节数组({string.Join(" ", bytes.Select(t => $"0x{t:X2}"))})失败！\r\n{ex.Message}", ex);
                return false;
            }
        }

        /// <summary>
        /// 按行发送并读取(字符串)
        /// </summary>
        /// <param name="data">要发送的字符串</param>
        /// <param name="iSendOut">发送超时时间，单位毫秒</param>
        /// <param name="iReadOut">读取超时时间，单位毫秒</param>
        /// <param name="sendDelay">发送后等待回读时间，单位毫秒。</param>
        /// <param name="sNewLine">行分隔字符串</param>
        /// <param name="isClosePort">命令完成后是否关闭串口</param>
        /// <returns></returns>
        protected virtual string SendAndReadLine(string data, int iSendOut, int iReadOut, long sendDelay = 0, string sNewLine = "\r\n", bool isClosePort = false)
        {
            string sRet = string.Empty;
            MyTimer timer = new MyTimer();
            try
            {
                lock (_LockObj)
                {
                    _StopFlag = false;
                    timer.AbortFun = () => _StopFlag;
                    if (!OpenPort()) return null;
                    if (!string.IsNullOrWhiteSpace(sNewLine)) _Port.NewLine = sNewLine;
                    _Port.WriteTimeout = iSendOut;
                    _Port.ReadTimeout = iReadOut;
                    _Port.WriteLine(data);
                    if (sendDelay > 0) timer.Delay(sendDelay);
                    if (_StopFlag) return null;
                    sRet = _Port.ReadLine();
                    return sRet;
                }
            }
            catch (Exception ex)
            {
                ClosePort();
                this._Logger.Error($"发送字符串数据({data})并读取返回({sRet})数据失败！\r\n{ex.Message}", ex);
                return string.Empty;
            }
        }

        /// <summary>
        /// 按行发送并读取(字节数组)
        /// </summary>
        /// <param name="bytes">要发送的字节数组</param>
        /// <param name="iSendOut">发送超时时间，单位毫秒</param>
        /// <param name="iReadOut">读取超时时间，单位毫秒</param>
        /// <param name="sendDelay">发送后等待回读时间，单位毫秒。</param>
        /// <param name="sNewLine">行分隔字符串</param>
        /// <param name="isClosePort">命令完成后是否关闭串口</param>
        /// <returns></returns>
        protected virtual IList<byte> SendAndReadLine(byte[] bytes, int iSendOut, int iReadOut, long sendDelay = 0, string sNewLine = "\r\n", bool isClosePort = false)
        {
            string data = Encoding.ASCII.GetString(bytes);
            string sRet = SendAndReadLine(data, iSendOut, iReadOut, sendDelay, sNewLine, isClosePort);
            return Encoding.ASCII.GetBytes(sRet);
        }

        /// <summary>
        /// 释放设备
        /// </summary>
        public virtual void Dispose()
        {
            ClosePort();
            _PortParametersString = string.Empty;
            _Port = null;
        }


    }
}
